//
//  ServerController.m
//  Server
//
//  Created by Kevin Schewior on 12/7/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "ServerController.h"


@implementation ServerController
    
-(id) initWithWindow:(NSWindow *)window{
    self = [super initWithWindow:window];
    if (self) {
        //init thread controller and set message delegate to receive incoming messages
        stc = [[ServerThreadController alloc] init];
        [stc setMessageDelegate:self];
    }
    
    return self;
}

-(void) dealloc{
    [super dealloc];
    //stop networking thread, then release object
    [stc stopServerThread];
    [stc release];
    //todo: release players etc
}

-(void)windowDidLoad{
    [super windowDidLoad];
}

-(IBAction) startServer:(id)sender{
    //initialize the dictionaries
    players = [[NSMutableDictionary alloc] initWithCapacity:10];
    checkpoints = [[NSMutableDictionary alloc] initWithCapacity:10];
    teams = [[NSMutableDictionary alloc] initWithCapacity:10];
    catchingTimers = [[NSMutableDictionary alloc] initWithCapacity:10];
    
    //create some testing checkpoints and teams
    Team *team1 = [[Team alloc] initWithName:@"0"]; 
    Team *team2 = [[Team alloc] initWithName:@"1"];
    Checkpoint *checkpoint1 = [[Checkpoint alloc]initWithID:@"0" andLong:6.058799 andLat:50.779587 andPoints:100];
    Checkpoint *checkpoint2 = [[Checkpoint alloc]initWithID:@"1" andLong:6.058973 andLat:50.77909 andPoints:1000];
    Checkpoint *checkpoint3 = [[Checkpoint alloc]initWithID:@"2" andLong:6.059212 andLat:50.778406 andPoints:500];
    
    team1.base_long = 6.059024;
    team1.base_lat = 50.778969;
    [teams setObject:team1 forKey:@"0"];

    
    team2.base_long = 6.060569;
    team2.base_lat = 50.778959;
    [teams setObject:team2 forKey:@"1"];


    [checkpoints setObject:checkpoint1  forKey:@"0"];    
    [checkpoints setObject:checkpoint2  forKey:@"1"];    
    [checkpoints setObject:checkpoint3  forKey:@"2"];
    
    NSLog(@"Dictionary: %@ - %@",[players class],players);
    
    //create a timer for sending keyframes
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendKeyframes:) userInfo:nil repeats:true];
     
    [stc startServerThreadOnPort:31775];
}

//***********************************************************
//Keyframes method called every x seconds
//***********************************************************

-(void) sendKeyframes:(NSTimer*) timer{
    NSLog(@"Dictionary: %@ - %@",[players class],players);
    
    NSArray* players_array = [players allValues];
    
    for(int i=0; i<[players_array count]; i++){
        //get player and his team
        Player* player = [players_array objectAtIndex:i];
        Team* team = [teams objectForKey:player.teamID];
        
        if([player isKindOfClass:[Runner class]]){
            Runner* runner = (Runner*)player;
            
            //get Target
            Checkpoint* target = [checkpoints objectForKey:runner.checkpointID];
            
            //send Runner Keyframes
            NetworkMessage* keyframeMessage = [NetworkMessageCreator runnerKeyframeMessageWithTargetLongitude:target.position_long targetLatitude:target.position_lat targetPoints:target.points teamMoney:team.current_money teamPoints:team.current_points  allowedToCatch:runner.allowed_to_catch carryingPoints:runner.carrying_points caught:runner.caught baseLongitude:team.base_long baseLatitude:team.base_lat nearbyBase:runner.allowed_to_bring_home];
            [stc sendMessage:keyframeMessage toClient:runner.clientID];
        }
        else if([player isKindOfClass:[Hunter class]]){
            Hunter* hunter = (Hunter*)player;
            
            //get Target
            Runner* target = [players objectForKey:hunter.runnerID];
            
            //send Hunter Keyframes
            NetworkMessage* keyframeMessage = [NetworkMessageCreator hunterKeyframeMessageWithTargetLongitude:target.last_position_long targetLatitude:target.last_position_lat targetPoints:target.carrying_points teamMoney:team.current_money teamPoints:team.current_points allowedToCatch:hunter.allowed_to_catch];
            [stc sendMessage:keyframeMessage toClient:hunter.clientID];
        }
        else{
            //send Operator Keyframes
            NetworkMessage* keyframeMessage = [NetworkMessageCreator operatorKeyframeMessageWithTeams:teams checkpoints:checkpoints];
            [stc sendMessage:keyframeMessage toClient:player.clientID];   
        }
    }
}

//***********************************************************
//Method handling incoming Messages
//***********************************************************

-(void) incomingMessage:(NetworkMessage*) msg fromClient:(NSString*) client {
    //delegate the messages to the different handling methods
	NSDictionary* dict;
    
    NSLog(@"Received Message: %@",[msg.data description]);
    
    switch(msg.type) {
		case kLoginMessage:
            //client wishes to log in
			dict = (NSDictionary*)msg.data;
			[self loginWithClientID:client andPlayerName:[dict objectForKey:@"name"] andTeamID:[dict objectForKey:@"teamID"] andRole:[[dict objectForKey:@"role"] intValue] andMsgID:msg.messageID];
			break;
			
		case kLogoutMessage:
            //client wishes to log out
			[self logoutWithClientID:client andMsgID:msg.messageID];	
			break;
			
        case kLocationMessage:
            //client sends his current location
            dict = (NSDictionary*)msg.data;
			[self updateLocationWithClientID:client andLong:[[dict objectForKey:@"longitude"] doubleValue] andLat:[[dict objectForKey:@"latitude"] doubleValue] andAccuracy:[[dict objectForKey:@"accuracy"] doubleValue]];
            break;
            
        case kAssignCPMessage:
            //operator assigns a runner to a checkpoint
            dict = (NSDictionary*)msg.data;
            [self assignCheckpoint:[dict objectForKey:@"cpID"] toRunner:[dict objectForKey:@"playerID"] assign:[[dict objectForKey:@"assign"] boolValue] byOperator:client];
            break;
            
        case kAssignRNMessage:
            //operator assigns a hunter to a runner
            dict = (NSDictionary*)msg.data;
            [self assignRunner:[dict objectForKey:@"runnerID"] toHunter:[dict objectForKey:@"hunterID"] assign:[[dict objectForKey:@"assign"] boolValue] byOperator:client];
            break;
        
        case kCatchMessage:
            //runner or hunter starts to catch the checkpoint or runner, respectively
            dict = (NSDictionary*)msg.data;
            [self startCatchWithPlayer:client andMsgID:msg.messageID];
            break;
            
        case kStopCatchMessage:
            //runner or hunter stops to catch the checkpoint or runner, respectively
            //todo
            break;
            
        case kBringHomeMessage:
            //runner brings home money
            dict = (NSDictionary*)msg.data;
            [self bringHomeWithPlayerID:client andMsgID:msg.messageID];            
            break;
            
        default:
            break;
    }
}

//***********************************************************
//Methods reacting on messages
//***********************************************************

-(void) clientDisconnected:(NSString*) client{ 
    Player* player = [players objectForKey:client];
    
    if(player!=nil)
        [self removePlayer:player];
}

-(void) loginWithClientID:(NSString*) clientID andPlayerName:(NSString*) playerName andTeamID:(NSString*) teamID andRole:(int) role andMsgID:(int) msgID{
	
    NSLog(@"Teams: %@", teams);
    
    bool wentOK = false;
    Player* player;
    Team* team;
	
    if(teamID != nil)
        team = [teams objectForKey:teamID];
    
    //add player to dicitonaries if possible
    if(team!=nil && [players objectForKey:playerName]==nil){
        switch(role){
            case 0:
                //runner
                    player = [[Runner alloc] initWithName:playerName andClientID:clientID andTeamID:teamID];
                break;
            case 1:
                //hunter
                    player = [[Hunter alloc] initWithName:playerName andClientID:clientID andTeamID:teamID];
                break;
            case 2:
                //operator
                    player = [[Operator alloc] initWithName:playerName andClientID:clientID andTeamID:teamID];
                break;
        }

        [players setObject:player forKey:clientID];
        [team.players setObject:player forKey:clientID];
        wentOK = true;        
    }
    
    //send kLoginReplyMessage
    NetworkMessage* replyMessage = [NetworkMessageCreator loginReplyMessageRefID:msgID success:wentOK];
    [stc sendMessage:replyMessage toClient:clientID];
}

-(void) logoutWithClientID:(NSString*) clientID andMsgID:(int) msgID{
    bool wentOK = false;
    
    //does the player exist?
    if(clientID != nil){
        Player* player = [players objectForKey:clientID];
        
        if(player!=nil){
            wentOK = true;
            [self removePlayer:player];
        }
    }
    
    //send kLogoutReplyMessage
    NetworkMessage* replyMessage = [NetworkMessageCreator logoutReplyMessageRefID:msgID success:wentOK];
    [stc sendMessage:replyMessage toClient:clientID];    
}

-(void) updateLocationWithClientID:(NSString*) clientID andLong:(double)lon andLat:(double)lat andAccuracy:(double) accuracy{
    LocatedPlayer* player = [players objectForKey:clientID];
    
    //does the player exist?    
    if(player == nil)
        return;
    
    //update player location data 
    player.last_position_long = lon;
    player.last_position_lat = lat;
    player.last_position_accuracy = accuracy;
    player.last_position_update = [NSDate date];        
	
    if([player isKindOfClass:[Runner class]]){
        //the player is a runner, check if he is nearby his checkpoint
        
        Runner* runner = (Runner*)player;
    
        [self sendCatchMessageWithAllow:[self catchDistanceBetweenPlayer:clientID andCheckpoint:runner.checkpointID] toPlayer:runner];

        [self sendBringHomeMessageWithAllow:[self catchDistanceBetweenPlayer:clientID andCheckpoint:runner.checkpointID] toPlayer:runner];        
        
        //also check for all players he is hunted by whether he is near to them now        
        for(int i=0; i<[runner.caughtBy count]; i++){
            NSString* hunterID = [runner.caughtBy objectAtIndex:i];
            Hunter* hunter = [players objectForKey:hunterID];
            if(hunter!=nil)
                [self sendCatchMessageWithAllow:[self catchDistanceBetweenPlayerA:clientID andPlayerB:hunterID] toPlayer:hunter];        
        }
        
    }
    else if([player isKindOfClass:[Hunter class]]){
        //the player is a hunter, check if he is nearby his checkpoint   
        
        Hunter* hunter = (Hunter*)player;
        
        [self sendCatchMessageWithAllow:[self catchDistanceBetweenPlayerA:clientID andPlayerB:hunter.runnerID] toPlayer:hunter];
    }
    
	NSLog(@"New Location of Client %@: lat: %f lng:%f accuracy:%f",clientID,lat,lon,accuracy);
}

-(void) sendBringHomeMessageWithAllow:(bool) allow toPlayer:(Runner*)runner {
    //sends kAllowBringHomeMessage and kDisallowBringHomeMessage if needed  
    
    NSString* clientID = runner.clientID;
    
    if(allow){
        //send kAllowCatchMessage if it has not yet been sent
        if(!runner.allowed_to_bring_home){
            NetworkMessage* allowMessage = [NetworkMessageCreator allowBringHomeMessage];
            [stc sendMessage:allowMessage toClient:clientID];  
            runner.allowed_to_bring_home = true;
        }
    }
    else{
        //send kDisallowCatchMessage if kAllowCatchMessage has been sent before
        if(runner.allowed_to_bring_home){
            NetworkMessage* disallowMessage = [NetworkMessageCreator disallowBringHomeMessage]; 
            [stc sendMessage:disallowMessage toClient:clientID];
            runner.allowed_to_bring_home = false;
        }
    }
}

-(void) sendCatchMessageWithAllow:(bool) allow toPlayer:(LocatedPlayer*)player {
    //sends kAllowCatchMessage and kDisallowCatchMessage if needed
    //also sends kFreeMessage (if the catching was already in progress)
    //furthermore stops catching timers if needed
    
    NSString* clientID = player.clientID;
    
    if(allow){
        //send kAllowCatchMessage if it has not yet been sent
        if(!player.allowed_to_catch){
            NetworkMessage* allowMessage = [NetworkMessageCreator allowCatchMessage];
            [stc sendMessage:allowMessage toClient:clientID];  
            player.allowed_to_catch = true;
        }
    }
    else{
        //send kDisallowCatchMessage if kAllowCatchMessage has been sent before
        if(player.allowed_to_catch){
            NetworkMessage* disallowMessage = [NetworkMessageCreator disallowCatchMessage]; 
            [stc sendMessage:disallowMessage toClient:clientID];
            player.allowed_to_catch = false;
            
            //test whether player was catching something
            NSTimer* timer = [catchingTimers objectForKey:clientID];
            
            if(timer!=nil){
                //if so:
                //first, stop the timer
                [timer invalidate];
                [catchingTimers removeObjectForKey:clientID];
                
                //if the player is a hunter, also inform the respective runner
                if([player isKindOfClass:[Hunter class]]){
                    Hunter* hunter = (Hunter*)player;
                    
                    //does runner indeed exist?
                    Runner* runner = [players objectForKey:hunter.runnerID];
                    
                    if(runner!=nil){
                        NetworkMessage* freeMessage = [NetworkMessageCreator freeMessage]; 
                        [stc sendMessage:freeMessage toClient:hunter.runnerID];
                    }
                }
            }
        }
    }    
}

-(bool) catchDistanceBetweenPlayerA:(NSString*)playerAID andPlayerB:(NSString*)playerBID{
    //returns whether two players are close enough for catching
    LocatedPlayer* playerA = [players objectForKey:playerAID];
    LocatedPlayer* playerB = [players objectForKey:playerBID];    
    
    double dist = [ServerHelper distanceBetween:playerA and:playerB];
    
    return dist<25;
}

-(bool) catchDistanceBetweenPlayer:(NSString*)playerID andCheckpoint:(NSString*)checkpointID{
    //returns whether a player is close enough to a checkpoint for catching
    LocatedPlayer* player = [players objectForKey:playerID];;
    Checkpoint* checkpoint = [checkpoints objectForKey:checkpointID];;
    
    double dist = [ServerHelper distanceBetween:player and:checkpoint];
    
    return dist<25;
}

-(bool) bringHomeDistanceForPlayer:(NSString*)playerID{
    //returns whether a player is closed enough to his teams' base
    LocatedPlayer* player = [players objectForKey:playerID];
    Team* team = [players objectForKey:player.teamID];    
    
    double dist = [ServerHelper distanceBetween:player and:team];
    
    return dist<25;
}

-(void) assignCheckpoint:(NSString*)checkpointID toRunner:(NSString*)runnerID assign:(bool)assign byOperator:(NSString*)operatorID{
    //do runner and checkpoint exist?
    if([checkpoints objectForKey:checkpointID] == nil) return;
    if([players objectForKey:runnerID] == nil) return;  
    
    //are those indeed runner, hunter and operator?
    if(![[players objectForKey:runnerID] isKindOfClass:[Runner class]]) return;
    if(![[players objectForKey:operatorID] isKindOfClass:[Operator class]]) return;
    if(![[checkpoints objectForKey:checkpointID] isKindOfClass:[Checkpoint class]]) return;
    
    //assign variables
    Checkpoint* checkpoint = [checkpoints objectForKey:checkpointID];
    Runner* runner = [players objectForKey:runnerID];
    Operator* operator = [players objectForKey:operatorID];      
    
    if(![runner.teamID isEqualToString:operator.teamID]) return;
    
    //checkpoint and runner exist; operator and runner are in the same team; runner is currently not caught
    //now add connection
    if(assign){        
        runner.checkpointID = checkpointID;
        [checkpoint.caughtBy addObject:runnerID];
        
        //send kAssignNoifyMessage
        NetworkMessage* notifyMessage = [NetworkMessageCreator assignNotifyMessageWithTargetLatitude:checkpoint.position_lat longitude:checkpoint.position_long points:checkpoint.points assign:true]; 
        [stc sendMessage:notifyMessage toClient:runnerID];
    }
    else{
        //check if correct checkpoint is disassigned
        if(![runner.checkpointID isEqualToString:checkpointID]) return;
        
        runner.checkpointID = nil;
        [checkpoint.caughtBy removeObject:runnerID];
        
        //send kAssignNoifyMessage
        NetworkMessage* notifyMessage = [NetworkMessageCreator assignNotifyMessageWithTargetLatitude:0 longitude:0 points:0 assign:false]; 
        [stc sendMessage:notifyMessage toClient:runnerID];
    }
}

-(void) assignRunner:(NSString*)runnerID toHunter:(NSString*)hunterID assign:(bool)assign byOperator:(NSString*)operatorID{
    //do hunter and runner exist?
    if([players objectForKey:runnerID] == nil) return;
    if([players objectForKey:hunterID] == nil) return;        
    if([players objectForKey:operatorID] == nil) return;        
    
    //are those indeed runner, hunter and operator?
    if(![[players objectForKey:runnerID] isKindOfClass:[Runner class]]) return;
    if(![[players objectForKey:operatorID] isKindOfClass:[Operator class]]) return;
    if(![[players objectForKey:hunterID] isKindOfClass:[Hunter class]]) return;
    
    //assign variables
    Hunter* hunter = [players objectForKey:hunterID];
    Runner* runner = [players objectForKey:runnerID];
    Operator* operator = [players objectForKey:operatorID];
    
    if(![hunter.teamID isEqualToString:operator.teamID]) return;
    
    if([runner.teamID isEqualToString:hunter.teamID]) return;
    
    //hunter and runner exist; operator and hunter are in the same team; runner and hunter are in different teams
    //now add or remove connection
    if(assign){  
        hunter.runnerID = runnerID;
        [runner.caughtBy addObject:hunterID];
        
        //send kAssignNoifyMessage
        NetworkMessage* notifyMessage = [NetworkMessageCreator assignNotifyMessageWithTargetLatitude:runner.last_position_lat longitude:runner.last_position_long points:runner.carrying_points assign:true]; 
        [stc sendMessage:notifyMessage toClient:hunterID];        
    }
    else{
        //check if correct runner is disassigned
        if(![hunter.runnerID isEqualToString:runnerID]) return;
        
        hunter.runnerID = nil;
        [runner.caughtBy removeObject:hunterID];
        //send kAssignNoifyMessage
        NetworkMessage* notifyMessage = [NetworkMessageCreator assignNotifyMessageWithTargetLatitude:0 longitude:0 points:0 assign:false]; 
        [stc sendMessage:notifyMessage toClient:hunterID];  
    }
}

-(void) startCatchWithPlayer:(NSString*)playerID andMsgID:(NSInteger)msgID{    
    Player* player = [players objectForKey:playerID];
    
    //does player exist?
    if(player == nil)
        return;    
    
    //only located players may catch
    if(![player isKindOfClass:[LocatedPlayer class]])
        return;
    
    LocatedPlayer* loc_player = (LocatedPlayer*) player;
    
    //is the player allowed to catch?
    if(!loc_player.allowed_to_catch)
        return;
    
    if([loc_player isKindOfClass:[Runner class]]){
        //the player is a runner
        Runner* runner = (Runner*) player;
        
        //does the player have a target and is he not caught?
        if(runner.checkpointID == nil || runner.caught)
            return;
        
        //send kCatchStartMessage to the runner
        //NetworkMessage* startMessage = [NetworkMessageCreator catchStartMessageWithRefID:msgID]; 
        //[stc sendMessage:startMessage toClient:playerID];          
        
        //start the timer and put it in a dictionary (so that it can be found and stopped later on)
        [catchingTimers setObject:[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(finishCatchingProcess:) userInfo:playerID repeats:false] forKey:playerID];
    }
    else{
        //the player is a hunter
        Hunter* hunter = (Hunter*) player;
        
        //does the player have a target?
        if(hunter.runnerID == nil)
            return;
        
        //send kCatchStartMessage to the hunter
        NetworkMessage* startMessage = [NetworkMessageCreator catchStartMessageWithRefID:msgID]; 
        [stc sendMessage:startMessage toClient:playerID];  
        
        //send kBeingCaughtMessage to the runner
        NetworkMessage* beingCaughtMessage = [NetworkMessageCreator catchStartMessageWithRefID:msgID]; 
        [stc sendMessage:beingCaughtMessage toClient:hunter.runnerID];  
        
        //start the timer and put it in a dictionary (so that it can be found and stopped later on)
        [catchingTimers setObject:[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(finishCatchingProcess:) userInfo:playerID repeats:false] forKey:playerID];
    }    
}

-(void) finishCatchingProcess:(NSTimer*) timer{
    //get the catching player
    NSString* playerID = [timer userInfo];
    if(playerID == nil)
        return;
    
    Player* player = [players objectForKey:playerID];    
    
    //remove the timer
    [catchingTimers removeObjectForKey:playerID];
    
    if([player isKindOfClass:[Runner class]]){
        //the player is a runner
        Runner* runner = (Runner*) player;     
        
        //does the target still exist?
        if(runner.checkpointID == nil)
            return;
        
        Checkpoint* checkpoint = [checkpoints objectForKey:runner.checkpointID];
        
        //remove connection
        runner.checkpointID = nil;
        runner.allowed_to_catch = false;
        [checkpoint.caughtBy removeObject:playerID];
        
        //send message
        //todo: add refid here        
        NetworkMessage* catchEndMessage = [NetworkMessageCreator catchEndMessage]; 
        [stc sendMessage:catchEndMessage toClient:playerID];        
        
        //change attributes
        runner.carrying_points += checkpoint.points;
    }
    else{
        //the player is a hunter
        Hunter* hunter = (Hunter*) player;   
        NSString* runnerID = hunter.runnerID;
        
        //does the target still exist?
        if(runnerID == nil)
            return;        
        
        Runner* runner = [players objectForKey:runnerID];
        
        //remove connection
        hunter.runnerID = nil;
        hunter.allowed_to_catch = false;
        [runner.caughtBy removeObject:playerID];        
        
        //remove connection from each hunter (!= the catching hunter) to the caught runner (and notify him)
        while([runner.caughtBy count]!=0){
            NSString* hunter1ID = [runner.caughtBy objectAtIndex:0];
            Hunter* hunter1 = [players objectForKey:hunter1ID];
            if(hunter!=nil && hunter1!=hunter){
                NetworkMessage* disassignMessage = [NetworkMessageCreator assignNotifyMessageWithTargetLatitude:0.0 longitude:0.0 points:0 assign:false]; 
                [stc sendMessage:disassignMessage toClient:hunter1ID];
            }
            [runner.caughtBy removeObjectAtIndex:0];
        }        
        
        //send messages
        //todo: add refid here
        NetworkMessage* catchEndMessage = [NetworkMessageCreator catchEndMessage]; 
        [stc sendMessage:catchEndMessage toClient:playerID];
        NetworkMessage* caughtMessage = [NetworkMessageCreator caughtMessage]; 
        [stc sendMessage:caughtMessage toClient:runnerID];        
        
        //change attributes
        runner.caught = true;
        Team* hunter_team = [teams objectForKey:hunter.teamID]; 
        hunter_team.current_money += runner.carrying_points;
        runner.carrying_points = 0;
        
        //start a timer making the runner free again
        [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(refree:) userInfo:runnerID repeats:false];       
    }
}

-(void) refree:(NSTimer*) timer{
    NSString* runnerID = [timer userInfo];
    if(runnerID == nil)
        return;

    //get the runner, make him free again
    Runner* runner = [players objectForKey:runnerID];
    runner.caught = false;
    
    //notify him
    NetworkMessage* freeMessage = [NetworkMessageCreator freeMessage]; 
    [stc sendMessage:freeMessage toClient:runnerID];    
}

-(void) bringHomeWithPlayerID:(NSString*)playerID andMsgID:(NSInteger)msgID{
    if([players objectForKey:playerID] == nil)
        return;
    
    if(![[players objectForKey:playerID] isKindOfClass:[Runner class]])
        return;
    
    //player is a runner and exists
    //now refresh the player/team attributes
    Runner* runner = [players objectForKey:playerID];
    
    if(runner.allowed_to_bring_home){
       Team* team = [teams objectForKey:runner.teamID];
       team.current_points += runner.carrying_points;
       runner.carrying_points = 0;
    }
    
    //notify him
    NetworkMessage* okMessage = [NetworkMessageCreator bringHomeOKMessageWithOK:runner.allowed_to_bring_home refid:msgID]; 
    [stc sendMessage:okMessage toClient:playerID];        
}

//***********************************************************
//Auxiliary Methods
//***********************************************************

-(void) removePlayer:(Player*) player{
    //delete player from dictionaries if possible
    NSString* teamID = player.teamID;
    Team* team = [teams objectForKey:teamID];
    
    //delete player as target
    if([player isKindOfClass:[Runner class]]){
        Runner* runner = (Runner*)player;
        
        for(int i=0; i<[runner.caughtBy count]; i++){
            NSString* hunterID = [runner.caughtBy objectAtIndex:i];
            Hunter* hunter = [players objectForKey:hunterID];
            if(hunter!=nil){
                hunter.runnerID = nil;
                
                //notify hunter
                NetworkMessage* disassignMessage = [NetworkMessageCreator assignNotifyMessageWithTargetLatitude:0.0 longitude:0.0 points:0 assign:false]; 
                [stc sendMessage:disassignMessage toClient:hunterID];
            }
        }        
    }
    
    [team.players removeObjectForKey:player.clientID];
    [players removeObjectForKey:player.clientID];
}

@end
